package com.googlecode.economini.converter;

import java.util.Collection;
import java.util.List;

import org.xml.sax.Attributes;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;

public class TokenizerNode <T extends Enum<?>> {
    private final String name;
    private final AttributeMatcher attributes;
    private final TokenFactory<T> tokenFactory;
    private final Multimap<String, TokenizerNode<T>> childMap;

    public static <T extends Enum<?>> TokenizerNode<T> buildNodeForPath(
            List<String> path,
            List<TokenizerNode<T>> nodes) {
        if (path == null || path.isEmpty()) {
            throw new IllegalArgumentException("Path must be specified");
        }

        if (path.size() == 1) {
          return new TokenizerNode<T>(
              path.get(0),
              AttributeMatcher.any(),
              null,
              nodes);
        }

        TokenizerNode<T> childNode = buildNodeForPath(
                path.subList(1, path.size()),
                nodes);

        return new TokenizerNode<T>(
                path.get(0),
                AttributeMatcher.any(),
                null,
                ImmutableList.of(childNode));
    }

    public static <T extends Enum<?>> TokenizerNode<T> build(
        String name,
        AttributeMatcher attributes,
        TokenFactory<T> tokenFactory,
        List<TokenizerNode<T>> children) {
      return new TokenizerNode<T>(name, attributes, tokenFactory, children);
    }

    public static <T extends Enum<?>> TokenizerNode<T> build(
        String name,
        AttributeMatcher attributes,
        TokenFactory<T> tokenFactory) {
      return new TokenizerNode<T>(
          name,
          attributes,
          tokenFactory,
          ImmutableList.<TokenizerNode<T>>of());
    }

    public TokenizerNode(
            String name,
            AttributeMatcher attributes,
            TokenFactory<T> tokenFactory,
            List<TokenizerNode<T>> children) {
        this.name = name;
        this.attributes = attributes;
        this.tokenFactory = tokenFactory;

        ImmutableMultimap.Builder<String, TokenizerNode<T>> childMapBuilder =
                ImmutableMultimap.builder();
        for (TokenizerNode<T> node : children) {
            childMapBuilder.put(node.getName(), node);
        }

        this.childMap = childMapBuilder.build();
    }

    public Token<T> buildToken(String element, Attributes attributes) {
        if (tokenFactory == null) {
            return null;
        }

        return tokenFactory.fromElement(element, attributes);
    }

    public TokenizerNode<T> getNodeFor(String name, Attributes attributes) {
        // TODO does this return null?
        Collection<TokenizerNode<T>> possibleChildren = childMap.get(name);

        for (TokenizerNode<T> child : possibleChildren) {
            if (child.getAttributes().doesMatch(attributes)) {
                return child;
            }
        }

        // Check for default matchers
        possibleChildren = childMap.get("");
        for (TokenizerNode<T> child : possibleChildren) {
            if (child.getAttributes().doesMatch(attributes)) {
                return child;
            }
        }

        return null;
    }

    public String getName() {
        return name;
    }

    public AttributeMatcher getAttributes() {
        return attributes;
    }

    @Override
    public String toString() {
        return name;
    }
}
